מדריך מקיף ל-Celery, תור משימות מבוזר, עם דוגמאות מעשיות לאינטגרציה עם Redis לעיבוד משימות אסינכרוני יעיל.
תור המשימות של Celery: עיבוד משימות מבוזר באמצעות אינטגרציה עם Redis
בעולם של היום, שבו יישומים הופכים למורכבים ותובעניים יותר, היכולת לטפל במשימות באופן אסינכרוני היא חיונית. Celery, תור משימות מבוזר רב עוצמה, מספק פתרון חזק להורדת משימות שגוזלות זמן רב או משאבים רבים מזרימת היישום הראשית. בשילוב עם Redis, מאגר נתונים רב-תכליתי בזיכרון, Celery מציע גישה יעילה וניתנת להרחבה לעיבוד משימות ברקע.
מה זה Celery?
Celery הוא תור משימות/עבודות אסינכרוני המבוסס על העברת הודעות מבוזרת. הוא משמש לביצוע משימות באופן אסינכרוני (ברקע) מחוץ לזרימת היישום הראשית. זהו דבר חיוני עבור:
- שיפור התגובתיות של היישום: על ידי הורדת משימות ל-workers של Celery, יישום הווב שלכם נשאר תגובתי ולא קופא בעת עיבוד פעולות מורכבות.
- סקלאביליות: Celery מאפשר לכם לפזר משימות על פני צמתים מרובים של workers, ולהרחיב את קיבולת העיבוד שלכם לפי הצורך.
- אמינות: Celery תומך בניסיונות חוזרים של משימות ובטיפול בשגיאות, מה שמבטיח שמשימות יושלמו בסופו של דבר גם במקרה של כשלים.
- טיפול במשימות ארוכות טווח: תהליכים שלוקחים זמן רב, כגון המרת וידאו, יצירת דוחות או שליחת מספר רב של הודעות דואר אלקטרוני, מתאימים באופן אידיאלי ל-Celery.
למה להשתמש ב-Redis עם Celery?
בעוד ש-Celery תומך במגוון רחב של ברוקרים להודעות (RabbitMQ, Redis וכו'), Redis הוא בחירה פופולרית בזכות פשטותו, מהירותו וקלות ההתקנה שלו. Redis פועל גם כברוקר ההודעות (transport) וגם, באופן אופציונלי, כמאגר התוצאות (result backend) עבור Celery. הנה הסיבות לכך ש-Redis מתאים היטב:
- מהירות: Redis הוא מאגר נתונים בזיכרון, המספק העברת הודעות ואחזור תוצאות מהירים במיוחד.
- פשטות: התקנה והגדרה של Redis הן פשוטות יחסית.
- שמירה (אופציונלי): Redis מציע אפשרויות שמירה (persistence), המאפשרות לשחזר משימות במקרה של כשל בברוקר.
- תמיכה ב-Pub/Sub: יכולות הפרסום/הרשמה (publish/subscribe) של Redis מתאימות היטב לארכיטקטורת העברת ההודעות של Celery.
רכיבי הליבה של Celery
הבנת רכיבי המפתח של Celery חיונית לניהול משימות יעיל:
- יישום Celery (celery): נקודת הכניסה הראשית לאינטראקציה עם Celery. הוא אחראי על הגדרת תור המשימות והתחברות לברוקר ולמאגר התוצאות.
- משימות (Tasks): פונקציות או מתודות המעוטרות ב-
@app.taskהמייצגות את יחידות העבודה שיש לבצע באופן אסינכרוני. - עובדים (Workers): תהליכים המבצעים את המשימות. ניתן להריץ מספר workers על מכונה אחת או יותר כדי להגדיל את קיבולת העיבוד.
- ברוקר (תור הודעות): המתווך שמעביר משימות מהיישום ל-workers. ניתן להשתמש ב-Redis, RabbitMQ וברוקרים אחרים.
- מאגר תוצאות (Result Backend): מאחסן את תוצאות המשימות. Celery יכול להשתמש ב-Redis, מסדי נתונים (כמו PostgreSQL או MySQL), או מאגרים אחרים לאחסון תוצאות.
התקנת Celery עם Redis
להלן מדריך שלב אחר שלב להתקנת Celery עם Redis:
1. התקנת תלויות
ראשית, התקינו את Celery ו-Redis באמצעות pip:
pip install celery redis
2. התקנת שרת Redis
התקינו את redis-server. ההוראות ישתנו בהתאם למערכת ההפעלה שלכם. לדוגמה, ב-Ubuntu:
sudo apt update
sudo apt install redis-server
עבור macOS (באמצעות Homebrew):
brew install redis
עבור Windows, ניתן להוריד את Redis מהאתר הרשמי של Redis או להשתמש ב-Chocolatey:
choco install redis
3. הגדרת Celery
צרו קובץ celeryconfig.py כדי להגדיר את Celery:
# celeryconfig.py
broker_url = 'redis://localhost:6379/0'
result_backend = 'redis://localhost:6379/0'
task_serializer = 'json'
result_serializer = 'json'
accept_content = ['json']
timezone = 'UTC'
enable_utc = True
הסבר:
broker_url: מציין את כתובת ה-URL של ברוקר ה-Redis. פורט ה-Redis המוגדר כברירת מחדל הוא 6379./0מייצג את מספר מסד הנתונים של Redis (0-15).result_backend: מציין את כתובת ה-URL של מאגר התוצאות של Redis, תוך שימוש באותה תצורה כמו הברוקר.task_serializerו-result_serializer: מגדירים את שיטת הסריאליזציה ל-JSON עבור משימות ותוצאות.accept_content: מפרט את סוגי התוכן המקובלים עבור משימות.timezoneו-enable_utc: מגדירים את הגדרות אזור הזמן. מומלץ להשתמש ב-UTC לעקביות בין שרתים שונים.
4. יצירת יישום Celery
צרו קובץ פייתון (לדוגמה, tasks.py) כדי להגדיר את יישום ה-Celery והמשימות שלכם:
# tasks.py
from celery import Celery
import time
app = Celery('my_tasks', broker='redis://localhost:6379/0', backend='redis://localhost:6379/0')
app.config_from_object('celeryconfig')
@app.task
def add(x, y):
time.sleep(5) # Simulate a long-running task
return x + y
@app.task
def send_email(recipient, subject, body):
# Simulate sending an email
print(f"Sending email to {recipient} with subject '{subject}' and body '{body}'")
time.sleep(2)
return f"Email sent to {recipient}"
הסבר:
Celery('my_tasks', broker=...): יוצר יישום Celery בשם 'my_tasks' ומגדיר את הברוקר וה-backend באמצעות כתובות URL. לחלופין, ניתן להשמיט את הארגומנטיםbrokerו-backendאם מגדירים אותם באופן בלעדי באמצעותapp.config_from_object('celeryconfig').@app.task: דקורטור שהופך פונקציית פייתון רגילה למשימת Celery.add(x, y): משימה פשוטה שמחברת שני מספרים וישנה למשך 5 שניות כדי לדמות פעולה ארוכה.send_email(recipient, subject, body): מדמה שליחת דואר אלקטרוני. בתרחיש אמיתי, זה יכלול התחברות לשרת דואר אלקטרוני ושליחת ההודעה.
5. הפעלת ה-worker של Celery
פתחו טרמינל ונווטו לספרייה המכילה את tasks.py ו-celeryconfig.py. לאחר מכן, הפעילו את ה-worker של Celery:
celery -A tasks worker --loglevel=info
הסבר:
celery -A tasks worker: מפעיל את ה-worker של Celery, ומציין את המודול (tasks) שבו מוגדרים יישום ה-Celery והמשימות שלכם.--loglevel=info: מגדיר את רמת הרישום ל-INFO, ומספק מידע מפורט על ביצוע המשימות.
6. שליחת משימות
בסקריפט פייתון אחר או במעטפת אינטראקטיבית, ייבאו את המשימות ושלחו אותן ל-worker של Celery:
# client.py
from tasks import add, send_email
# Send the 'add' task asynchronously
result = add.delay(4, 5)
print(f"Task ID: {result.id}")
# Send the 'send_email' task asynchronously
email_result = send_email.delay('user@example.com', 'Hello', 'This is a test email.')
print(f"Email Task ID: {email_result.id}")
# Later, you can retrieve the result:
# print(result.get())
הסבר:
add.delay(4, 5): שולח את משימתaddל-worker של Celery עם הארגומנטים 4 ו-5. משתמשים במתודהdelay()כדי לבצע את המשימה באופן אסינכרוני. היא מחזירה אובייקטAsyncResult.result.id: מספק את המזהה הייחודי של המשימה, שניתן להשתמש בו למעקב אחר התקדמותה.result.get(): חוסם את הריצה עד לסיום המשימה ומחזיר את התוצאה. השתמשו בזהירות ב-thread הראשי מכיוון שזה מנוגד למטרת עיבוד המשימות האסינכרוני.
7. ניטור סטטוס משימות (אופציונלי)
ניתן לנטר את סטטוס המשימות באמצעות אובייקט AsyncResult. תצטרכו להסיר את ההערה ולהריץ את `result.get()` בדוגמה לעיל כדי לראות את התוצאה המוחזרת לאחר השלמת המשימה, או להשתמש בשיטת ניטור אחרת.
Celery מציע גם כלים כמו Flower לניטור בזמן אמת. Flower הוא כלי ניטור וניהול מבוסס ווב עבור Celery.
כדי להתקין את Flower:
pip install flower
כדי להפעיל את Flower:
celery -A tasks flower
Flower בדרך כלל ירוץ על http://localhost:5555. אז תוכלו לנטר את סטטוס המשימות, סטטוס ה-workers ומדדים אחרים של Celery דרך ממשק הווב של Flower.
תכונות מתקדמות של Celery
Celery מציע מגוון רחב של תכונות מתקדמות לניהול ואופטימיזציה של תור המשימות שלכם:
ניתוב משימות
ניתן לנתב משימות ל-workers ספציפיים על בסיס שמם, תורים או קריטריונים אחרים. זה שימושי לחלוקת משימות על בסיס דרישות משאבים או עדיפות. ניתן להשיג זאת באמצעות CELERY_ROUTES בקובץ celeryconfig.py שלכם. לדוגמה:
# celeryconfig.py
CELERY_ROUTES = {
'tasks.add': {'queue': 'priority_high'},
'tasks.send_email': {'queue': 'emails'},
}
לאחר מכן, בעת הפעלת ה-worker, ציינו לאילו תורים הוא צריך להאזין:
celery -A tasks worker -Q priority_high,emails --loglevel=info
תזמון משימות (Celery Beat)
Celery Beat הוא מתזמן שמוסיף משימות לתור באופן תקופתי. הוא משמש למשימות שצריכות להתבצע במרווחי זמן קבועים (לדוגמה, דוחות יומיים, גיבויים שעתיים). מגדירים אותו באמצעות CELERY_BEAT_SCHEDULE בקובץ celeryconfig.py שלכם.
# celeryconfig.py
from celery.schedules import crontab
CELERY_BEAT_SCHEDULE = {
'add-every-30-seconds': {
'task': 'tasks.add',
'schedule': 30.0,
'args': (16, 16)
},
'send-daily-report': {
'task': 'tasks.send_email',
'schedule': crontab(hour=7, minute=30), # Executes every day at 7:30 AM UTC
'args': ('reports@example.com', 'Daily Report', 'Here is the daily report.')
},
}
כדי להפעיל את Celery Beat:
celery -A tasks beat --loglevel=info
שימו לב: Beat צריך מקום לאחסן מתי הוא הריץ לאחרונה משימה מתוזמנת. ברירת המחדל היא שימוש במסד נתונים מבוסס קבצים (celerybeat-schedule), שאינו מתאים לסביבות ייצור. לייצור, השתמשו במתזמן מגובה מסד נתונים (למשל, Redis).
ניסיונות חוזרים למשימות
Celery יכול לנסות מחדש באופן אוטומטי משימות שנכשלו. זה שימושי לטיפול בשגיאות חולפות (למשל, תקלות רשת, הפסקות זמניות של מסד הנתונים). ניתן להגדיר את מספר הניסיונות החוזרים וההשהיה ביניהם באמצעות האפשרויות retry_backoff ו-max_retries בדקורטור @app.task.
@app.task(bind=True, max_retries=5, retry_backoff=True)
def my_task(self, arg1, arg2):
try:
# Some potentially failing operation
result = perform_operation(arg1, arg2)
return result
except Exception as exc:
self.retry(exc=exc, countdown=5) # Retry after 5 seconds
הסבר:
bind=True: מאפשר למשימה לגשת להקשר שלה (כולל המתודהretry).max_retries=5: מגדיר את המספר המרבי של ניסיונות חוזרים ל-5.retry_backoff=True: מאפשר השהיה אקספוננציאלית (exponential backoff) לניסיונות חוזרים (ההשהיה גדלה עם כל ניסיון). ניתן גם לציין השהיה קבועה באמצעותretry_backoff=Falseיחד עם ארגומנטdefault_retry_delay.self.retry(exc=exc, countdown=5): מנסה מחדש את המשימה לאחר 5 שניות. הארגומנטexcהוא החריגה שגרמה לכשל.
שירשור משימות וזרימות עבודה
Celery מאפשר לשרשר משימות יחד כדי ליצור זרימות עבודה מורכבות. זה שימושי למשימות התלויות בפלט של משימות אחרות. ניתן להשתמש בפרימיטיבים chain, group ו-chord להגדרת זרימות עבודה.
Chain (שרשרת): מבצעת משימות באופן סדרתי.
from celery import chain
workflow = chain(add.s(4, 4), multiply.s(8))
result = workflow.delay()
print(result.get()) # Output: 64
בדוגמה זו, add.s(4, 4) יוצר חתימה של משימת add עם הארגומנטים 4 ו-4. באופן דומה, multiply.s(8) יוצר חתימה של משימת multiply עם הארגומנט 8. הפונקציה chain משלבת חתימות אלו לזרימת עבודה שמבצעת תחילה את add(4, 4), ואז מעבירה את התוצאה (8) ל-multiply(8).
Group (קבוצה): מבצעת משימות במקביל.
from celery import group
parallel_tasks = group(add.s(2, 2), multiply.s(3, 3), send_email.s('test@example.com', 'Parallel Tasks', 'Running in parallel'))
results = parallel_tasks.delay()
# To get results, wait for all tasks to complete
for res in results.get():
print(res)
Chord (אקורד): מבצע קבוצת משימות במקביל, ואז מבצע משימת callback עם תוצאות הקבוצה. זה שימושי כאשר יש צורך לצבור את התוצאות של משימות מרובות.
from celery import group, chord
header = group(add.s(i, i) for i in range(10))
callback = send_email.s('aggregation@example.com', 'Chord Result', 'Here are the aggregated results.')
workflow = chord(header)(callback)
result = workflow.delay()
# The callback task (send_email) will execute after all tasks in the header (add) are completed
# with the results passed to it.
טיפול בשגיאות
Celery מספק מספר דרכים לטפל בשגיאות:
- ניסיונות חוזרים למשימות: כפי שהוזכר קודם, ניתן להגדיר משימות לנסות שוב באופן אוטומטי במקרה של כשל.
- Error Callbacks: ניתן להגדיר callbacks לשגיאות שמתבצעים כאשר משימה נכשלת. אלה מוגדרים באמצעות הארגומנט
link_errorב-apply_async,delay, או כחלק משרשרת. - טיפול גלובלי בשגיאות: ניתן להגדיר את Celery לשלוח דוחות שגיאה לשירות ניטור (למשל, Sentry, Airbrake).
@app.task(bind=True)
def my_task(self, arg1, arg2):
try:
result = perform_operation(arg1, arg2)
return result
except Exception as exc:
# Log the error or send an error report
print(f"Task failed with error: {exc}")
raise
@app.task
def error_handler(request, exc, traceback):
print(f"Task {request.id} failed: {exc}\n{traceback}")
#Example usage
my_task.apply_async((1, 2), link_error=error_handler.s())
שיטות עבודה מומלצות לשימוש ב-Celery עם Redis
כדי להבטיח ביצועים ואמינות מיטביים, עקבו אחר שיטות העבודה המומלצות הבאות:
- השתמשו בשרת Redis אמין: לסביבות ייצור, השתמשו בשרת Redis ייעודי עם ניטור וגיבויים נאותים. שקלו להשתמש ב-Redis Sentinel לזמינות גבוהה.
- כיילו את תצורת Redis: התאימו את פרמטרי התצורה של Redis (למשל, מגבלות זיכרון, מדיניות פינוי) בהתבסס על צרכי היישום שלכם.
- נטרו את ה-workers של Celery: נטרו את תקינות וביצועי ה-workers של Celery כדי לזהות ולפתור בעיות במהירות. השתמשו בכלים כמו Flower או Prometheus לניטור.
- בצעו אופטימיזציה לסריאליזציה של משימות: בחרו שיטת סריאליזציה מתאימה (למשל, JSON, pickle) בהתבסס על המורכבות והגודל של ארגומנטי המשימות והתוצאות שלכם. היו מודעים להשלכות האבטחה בעת שימוש ב-pickle, במיוחד עם נתונים לא מהימנים.
- שמרו על משימות אידמפוטנטיות: ודאו שהמשימות שלכם הן אידמפוטנטיות, כלומר שניתן לבצע אותן מספר פעמים מבלי לגרום לתופעות לוואי בלתי רצויות. זה חשוב במיוחד למשימות שעלולות להיות מנוסות שוב לאחר כשל.
- טפלו בחריגות באלגנטיות: הטמיעו טיפול נאות בשגיאות במשימות שלכם כדי למנוע קריסות בלתי צפויות ולהבטיח ששגיאות נרשמות או מדווחות כראוי.
- השתמשו בסביבות וירטואליות: השתמשו תמיד בסביבות וירטואליות עבור פרויקטי הפייתון שלכם כדי לבודד תלויות ולמנוע התנגשויות.
- שמרו על Celery ו-Redis מעודכנים: עדכנו באופן קבוע את Celery ו-Redis לגרסאות האחרונות כדי להפיק תועלת מתיקוני באגים, עדכוני אבטחה ושיפורי ביצועים.
- ניהול תורים נכון: הקצו תורים ספציפיים לסוגי משימות שונים (למשל, משימות בעדיפות גבוהה, משימות עיבוד ברקע). זה מאפשר לתעדף ולנהל משימות בצורה יעילה יותר.
שיקולים בינלאומיים
בעת שימוש ב-Celery בהקשרים בינלאומיים, שקלו את הדברים הבאים:
- אזורי זמן: ודאו שה-workers של Celery ושרת ה-Redis שלכם מוגדרים עם אזור הזמן הנכון. השתמשו ב-UTC לעקביות בין אזורים שונים.
- לוקליזציה: אם המשימות שלכם כוללות עיבוד או יצירת תוכן מותאם מקומית, ודאו של-workers של Celery יש גישה לנתוני ה-locale והספריות הדרושים.
- קידוד תווים: השתמשו בקידוד UTF-8 עבור כל הארגומנטים והתוצאות של המשימות כדי לתמוך במגוון רחב של תווים.
- תקנות פרטיות נתונים: היו מודעים לתקנות פרטיות נתונים (למשל, GDPR) בעת עיבוד נתונים אישיים במשימות שלכם. הטמיעו אמצעי אבטחה מתאימים להגנה על מידע רגיש.
- חביון רשת (Network Latency): קחו בחשבון את חביון הרשת בין שרת היישום, ה-workers של Celery ושרת ה-Redis, במיוחד אם הם ממוקמים באזורים גיאוגרפיים שונים. בצעו אופטימיזציה של תצורת הרשת ושקלו להשתמש באשכול Redis מבוזר גיאוגרפית לשיפור הביצועים.
דוגמאות מהעולם האמיתי
הנה כמה דוגמאות מהעולם האמיתי לאופן שבו ניתן להשתמש ב-Celery ו-Redis לפתרון בעיות נפוצות:
- פלטפורמת מסחר אלקטרוני: עיבוד הזמנות, שליחת אישורי הזמנה, יצירת חשבוניות ועדכון מלאי ברקע.
- יישום מדיה חברתית: עיבוד העלאות תמונות, שליחת התראות, יצירת פידים מותאמים אישית וניתוח נתוני משתמשים.
- יישום שירותים פיננסיים: עיבוד עסקאות, יצירת דוחות, ביצוע הערכות סיכונים ושליחת התרעות.
- פלטפורמה חינוכית: בדיקת מטלות, יצירת תעודות, שליחת תזכורות לקורסים וניתוח ביצועי תלמידים.
- פלטפורמת IoT: עיבוד נתוני חיישנים, שליטה במכשירים, יצירת התרעות וניתוח ביצועי מערכת. לדוגמה, שקלו תרחיש חקלאות חכמה. ניתן להשתמש ב-Celery לעיבוד קריאות חיישנים מחוות באזורים שונים (למשל, ברזיל, הודו, אירופה) ולהפעיל מערכות השקיה אוטומטיות על בסיס קריאות אלו.
סיכום
Celery, בשילוב עם Redis, מספק פתרון עוצמתי ורב-תכליתי לעיבוד משימות מבוזר. על ידי הורדת משימות שגוזלות זמן רב או משאבים רבים ל-workers של Celery, ניתן לשפר את התגובתיות, הסקלאביליות והאמינות של היישום. עם מערך התכונות העשיר ואפשרויות התצורה הגמישות שלו, ניתן להתאים את Celery למגוון רחב של מקרי שימוש, ממשימות רקע פשוטות ועד לזרימות עבודה מורכבות. אימוץ Celery ו-Redis פותח את הפוטנציאל לבניית יישומים בעלי ביצועים גבוהים וסקלאביליות, המסוגלים להתמודד עם עומסי עבודה מגוונים ותובעניים.